package com.bckj.fastboot.core.extra.relational;

import com.bckj.fastboot.core.sql.Sql;
import com.bckj.fastboot.core.sql.SqlCondition;

import java.util.*;
import java.util.function.Consumer;

/**
 * FilterBuilder
 *
 * @author wfc
 **/
public class Criteria implements FilterBuilder<Criteria> {

    private boolean isOr = false;

    private SqlCondition whereCondition = SqlCondition.NOT_NULL;

    public static Criteria of(Sql.WhereNode whereNode) {
        return new Criteria(whereNode, new HashMap<>());
    }

    private final Sql.WhereNode whereNode;

    public final Map<String, Object> params;

    public Criteria(Sql.WhereNode whereNode, Map<String, Object> params) {
        this.whereNode = whereNode;
        this.params = params;
    }

    public Criteria whereCondition(SqlCondition whereCondition) {
        this.whereCondition = whereCondition;
        return this;
    }

    public Map<String, Object> params() {
        return params;
    }

    @Override
    public Criteria getChildren() {
        return this;
    }

    private void addWhere(String sqlPart) {
        if (isOr) {
            whereNode.or(sqlPart);
            isOr = false;
        } else {
            whereNode.and(sqlPart);
        }
    }

    @Override
    public Criteria where(String field, Object value, String opt) {
        if (whereCondition.isSatisfiedBy(value)) {
            String part = "%s %s %s".formatted(field, opt, formatValue(field, value));
            addWhere(part);
            params.put(field, value);
        }
        return this;
    }

    @Override
    public Criteria eq(String field, Object value) {
        return where(field, value, "=");
    }

    @Override
    public Criteria ne(String field, Object value) {
        return where(field, value, "<>");
    }

    @Override
    public Criteria gt(String field, Object value) {
        return where(field, value, ">");
    }

    @Override
    public Criteria ge(String field, Object value) {
        return where(field, value, ">=");
    }

    @Override
    public Criteria lt(String field, Object value) {
        return where(field, value, "<");
    }

    @Override
    public Criteria le(String field, Object value) {
        return where(field, value, "<=");
    }

    @Override
    public Criteria like(String field, Object value) {
        if (whereCondition.isSatisfiedBy(value)) {
            String part = field + " LIKE CONCAT('%'," + formatValue(field, value) + ",'%')";
            addWhere(part);
            params.put(field, value);
        }
        return this;
    }

    @Override
    public Criteria likeLeft(String field, Object value) {
        if (whereCondition.isSatisfiedBy(value)) {
            String part = field + " LIKE CONCAT('%'," + formatValue(field, value) + ")";
            addWhere(part);
            params.put(field, value);
        }
        return this;
    }

    @Override
    public Criteria likeRight(String field, Object value) {
        if (whereCondition.isSatisfiedBy(value)) {
            String part = field + " LIKE CONCAT(" + formatValue(field, value) + ",'%')";
            addWhere(part);
            params.put(field, value);
        }
        return this;
    }

    @Override
    public Criteria between(String field, Object val1, Object val2) {
        if (whereCondition.isSatisfiedBy(val1) && whereCondition.isSatisfiedBy(val2)) {
            String p1 = field + "1";
            String p2 = field + "2";
            String part = "%s BETWEEN %s AND %s".formatted(field, formatValue(p1, val1), formatValue(p2, val2));
            addWhere(part);
            params.put(p1, val1);
            params.put(p2, val2);
        }
        return this;
    }

    @Override
    public Criteria in(String field, Collection<?> coll) {
        if (whereCondition.isSatisfiedBy(coll)) {
            String part = "%s IN (%s)".formatted(field, formatValue(field, coll));
            addWhere(part);
            params.put(field, coll);
        }
        return this;
    }

    @Override
    public Criteria notIn(String field, Collection<?> coll) {
        if (whereCondition.isSatisfiedBy(coll)) {
            String part = "%s NOT IN (%s)".formatted(field, formatValue(field, coll));
            addWhere(part);
            params.put(field, coll);
        }
        return this;
    }

    @Override
    public Criteria isNull(String column) {
        String part = column + " IS NULL";
        addWhere(part);
        return this;
    }

    @Override
    public Criteria isNotNull(String column) {
        String part = column + " IS NOT NULL";
        addWhere(part);
        return this;
    }

    @Override
    public Criteria and(Consumer<FilterBuilder<?>> consumer) {
        Sql.WhereNode where = Sql.WhereNode.of();
        Criteria builder = new Criteria(where, this.params);
        builder.whereCondition(whereCondition);
        consumer.accept(builder);
        whereNode.group(where);
        return this;
    }

    @Override
    public Criteria or() {
        isOr = true;
        return this;
    }

    String formatValue(String field, Object value) {
        if (value == null) {
            return "NULL";
        }
        return ":" + field;
    }

}
